package org.example.xsda.service;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.xsda.common.PageResult;
import org.example.xsda.dto.FileAuditDTO;
import org.example.xsda.dto.FileUploadDTO;
import org.example.xsda.entity.FileInfo;
import org.example.xsda.entity.Student;
import org.example.xsda.entity.SysUser;
import org.example.xsda.exception.BusinessException;
import org.example.xsda.mapper.FileInfoMapper;
import org.example.xsda.mapper.StudentMapper;
import org.example.xsda.mapper.SysUserMapper;
import org.example.xsda.util.OssUtil;
import org.example.xsda.vo.FileInfoVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文件管理服务
 */
@Slf4j
@Service
public class FileService {
    
    @Resource
    private FileInfoMapper fileInfoMapper;
    
    @Resource
    private StudentMapper studentMapper;
    
    @Resource
    private SysUserMapper userMapper;
    
    @Resource
    private OssUtil ossUtil;
    
    @Resource
    private MessageService messageService;
    
    @Value("${file.storage.mode:local}")
    private String storageMode; // local=本地存储, oss=阿里云OSS
    
    @Value("${file.storage.local-path}")
    private String localStoragePath;
    
    @Value("${file.storage.url-prefix}")
    private String urlPrefix;
    
    /**
     * 初始化：确保本地存储目录存在
     */
    @PostConstruct
    public void init() {
        if ("local".equalsIgnoreCase(storageMode)) {
            File storageDir = new File(localStoragePath);
            if (!storageDir.exists()) {
                boolean created = storageDir.mkdirs();
                if (created) {
                    log.info("本地存储目录创建成功：{}", localStoragePath);
                } else {
                    log.error("本地存储目录创建失败：{}", localStoragePath);
                }
            } else {
                log.info("本地存储目录已存在：{}", localStoragePath);
            }
            log.info("文件存储模式：本地存储（local）");
        } else {
            log.info("文件存储模式：阿里云OSS（oss）");
        }
    }
    
    /**
     * 上传文件
     */
    @Transactional(rollbackFor = Exception.class)
    public FileInfoVO uploadFile(MultipartFile file, FileUploadDTO dto) {
        try {
            // 验证文件
            validateFile(file);
            
            String fileName = file.getOriginalFilename();
            String fileUrl;
            
            // 根据配置的存储模式决定上传方式
            if ("oss".equalsIgnoreCase(storageMode)) {
                // 使用阿里云OSS存储
                String folder = buildFolderPath(dto.getCategory(), dto.getFileType());
                fileUrl = ossUtil.upload(file, folder);
                log.info("文件上传到OSS：{}", fileName);
            } else {
                // 使用本地存储（默认）
                fileUrl = saveToLocal(file, dto.getCategory(), dto.getFileType());
                log.info("文件保存到本地：{}", fileName);
            }
            
            // 保存文件信息
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(fileName);
            fileInfo.setFileUrl(fileUrl);
            fileInfo.setFileSize(file.getSize());
            fileInfo.setFileType(dto.getFileType());
            fileInfo.setCategory(dto.getCategory());
            fileInfo.setStudentId(dto.getStudentId());
            fileInfo.setApprovalId(dto.getApprovalId());
            fileInfo.setApprovalRequired(dto.getApprovalRequired() != null ? dto.getApprovalRequired() : 0);
            fileInfo.setUploadUser(StpUtil.getLoginIdAsLong());
            
            // 如果需要审核，设置审核状态为待审核
            if (dto.getApprovalRequired() != null && dto.getApprovalRequired() == 1) {
                fileInfo.setAuditStatus(0);
            } else {
                fileInfo.setAuditStatus(1); // 不需要审核的直接通过
            }
            
            fileInfoMapper.insert(fileInfo);
            
            log.info("文件上传成功：{}, fileId: {}", file.getOriginalFilename(), fileInfo.getId());
            
            // 如果需要审核，发送通知给管理员
            if (dto.getApprovalRequired() != null && dto.getApprovalRequired() == 1) {
                sendAuditNotification(fileInfo);
            }
            
            return convertToVO(fileInfo);
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new BusinessException("文件上传失败：" + e.getMessage());
        }
    }
    
    /**
     * 批量上传文件
     */
    @Transactional(rollbackFor = Exception.class)
    public List<FileInfoVO> uploadFiles(List<MultipartFile> files, FileUploadDTO dto) {
        List<FileInfoVO> result = new ArrayList<>();
        for (MultipartFile file : files) {
            result.add(uploadFile(file, dto));
        }
        return result;
    }
    
    /**
     * 分页查询文件列表
     */
    public PageResult<FileInfoVO> getFilePage(Integer current, Integer size, String category, 
                                               String fileType, Integer auditStatus, Long studentId) {
        Page<FileInfo> page = new Page<>(current, size);
        
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(category != null, FileInfo::getCategory, category);
        wrapper.eq(fileType != null, FileInfo::getFileType, fileType);
        wrapper.eq(auditStatus != null, FileInfo::getAuditStatus, auditStatus);
        wrapper.eq(studentId != null, FileInfo::getStudentId, studentId);
        wrapper.orderByDesc(FileInfo::getCreateTime);
        
        IPage<FileInfo> filePage = fileInfoMapper.selectPage(page, wrapper);
        List<FileInfoVO> voList = convertToVOList(filePage.getRecords());
        
        return new PageResult<>(filePage.getTotal(), voList);
    }
    
    /**
     * 获取我上传的文件
     */
    public PageResult<FileInfoVO> getMyFiles(Integer current, Integer size, String category) {
        Long userId = StpUtil.getLoginIdAsLong();
        
        Page<FileInfo> page = new Page<>(current, size);
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getUploadUser, userId);
        wrapper.eq(category != null, FileInfo::getCategory, category);
        wrapper.orderByDesc(FileInfo::getCreateTime);
        
        IPage<FileInfo> filePage = fileInfoMapper.selectPage(page, wrapper);
        List<FileInfoVO> voList = convertToVOList(filePage.getRecords());
        
        return new PageResult<>(filePage.getTotal(), voList);
    }
    
    /**
     * 获取待审核文件列表
     */
    public PageResult<FileInfoVO> getPendingAuditFiles(Integer current, Integer size) {
        Page<FileInfo> page = new Page<>(current, size);
        
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getAuditStatus, 0);
        wrapper.eq(FileInfo::getApprovalRequired, 1);
        wrapper.orderByDesc(FileInfo::getCreateTime);
        
        IPage<FileInfo> filePage = fileInfoMapper.selectPage(page, wrapper);
        List<FileInfoVO> voList = convertToVOList(filePage.getRecords());
        
        return new PageResult<>(filePage.getTotal(), voList);
    }
    
    /**
     * 审核文件
     */
    @Transactional(rollbackFor = Exception.class)
    public void auditFile(FileAuditDTO dto) {
        FileInfo fileInfo = fileInfoMapper.selectById(dto.getFileId());
        if (fileInfo == null) {
            throw new BusinessException("文件不存在");
        }
        
        if (fileInfo.getAuditStatus() != 0) {
            throw new BusinessException("该文件已审核");
        }
        
        fileInfo.setAuditStatus(dto.getAuditStatus());
        fileInfo.setAuditRemark(dto.getAuditRemark());
        fileInfo.setAuditorId(StpUtil.getLoginIdAsLong());
        fileInfo.setAuditTime(LocalDateTime.now());
        
        fileInfoMapper.updateById(fileInfo);
        
        // 发送审核结果通知
        sendAuditResultNotification(fileInfo);
        
        log.info("文件审核完成：fileId: {}, status: {}", dto.getFileId(), dto.getAuditStatus());
    }
    
    /**
     * 获取文件详情
     */
    public FileInfoVO getFileDetail(Long id) {
        FileInfo fileInfo = fileInfoMapper.selectById(id);
        if (fileInfo == null) {
            throw new BusinessException("文件不存在");
        }
        return convertToVO(fileInfo);
    }
    
    /**
     * 删除文件
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteFile(Long id) {
        FileInfo fileInfo = fileInfoMapper.selectById(id);
        if (fileInfo == null) {
            throw new BusinessException("文件不存在");
        }
        
        // 检查权限：只有上传者或管理员可以删除
        Long currentUserId = StpUtil.getLoginIdAsLong();
        List<String> roles = StpUtil.getRoleList();
        if (!fileInfo.getUploadUser().equals(currentUserId) && !roles.contains("admin")) {
            throw new BusinessException("无权限删除此文件");
        }
        
        // 删除文件（根据文件URL判断是OSS还是本地）
        try {
            String fileUrl = fileInfo.getFileUrl();
            if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
                // OSS文件
                ossUtil.delete(fileUrl);
                log.info("删除OSS文件成功：{}", fileUrl);
            } else {
                // 本地文件
                deleteLocalFile(fileUrl);
                log.info("删除本地文件成功：{}", fileUrl);
            }
        } catch (Exception e) {
            log.error("删除文件失败", e);
            // 即使删除文件失败，也继续删除数据库记录
        }
        
        // 删除数据库记录
        fileInfoMapper.deleteById(id);
        
        log.info("文件删除成功：fileId: {}", id);
    }
    
    /**
     * 获取学生的所有文件
     */
    public List<FileInfoVO> getStudentFiles(Long studentId, String category) {
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getStudentId, studentId);
        wrapper.eq(category != null, FileInfo::getCategory, category);
        wrapper.eq(FileInfo::getAuditStatus, 1); // 只返回审核通过的
        wrapper.orderByDesc(FileInfo::getCreateTime);
        
        List<FileInfo> files = fileInfoMapper.selectList(wrapper);
        return convertToVOList(files);
    }
    
    // ========== 私有方法 ==========
    
    /**
     * 保存文件到本地
     */
    private String saveToLocal(MultipartFile file, String category, String fileType) throws IOException {
        // 构建保存路径：年份/类别/文件类型/
        String year = DateTimeFormatter.ofPattern("yyyy").format(LocalDateTime.now());
        String month = DateTimeFormatter.ofPattern("MM").format(LocalDateTime.now());
        String relativePath = year + File.separator + month + File.separator + 
                              category + File.separator + fileType + File.separator;
        
        // 创建完整目录
        String fullDirPath = localStoragePath + File.separator + relativePath;
        File dir = new File(fullDirPath);
        if (!dir.exists()) {
            boolean created = dir.mkdirs();
            if (!created) {
                throw new IOException("创建目录失败：" + fullDirPath);
            }
        }
        
        // 生成唯一文件名：时间戳_UUID_原始文件名
        String originalFilename = file.getOriginalFilename();
        String timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
        String uuid = UUID.randomUUID().toString().substring(0, 8);
        String newFileName = timestamp + "_" + uuid + "_" + originalFilename;
        
        // 保存文件
        String fullFilePath = fullDirPath + newFileName;
        Path targetPath = Paths.get(fullFilePath);
        file.transferTo(targetPath);
        
        // 返回相对路径URL（用于访问）
        String fileUrl = urlPrefix + "/" + relativePath.replace(File.separator, "/") + newFileName;
        
        log.info("文件保存到本地：{}", fullFilePath);
        return fileUrl;
    }
    
    /**
     * 删除本地文件
     */
    private void deleteLocalFile(String fileUrl) {
        try {
            // 从URL中提取相对路径
            String relativePath = fileUrl.replace(urlPrefix + "/", "");
            String fullPath = localStoragePath + File.separator + relativePath.replace("/", File.separator);
            
            File file = new File(fullPath);
            if (file.exists()) {
                boolean deleted = file.delete();
                if (deleted) {
                    log.info("本地文件删除成功：{}", fullPath);
                } else {
                    log.warn("本地文件删除失败：{}", fullPath);
                }
            } else {
                log.warn("本地文件不存在：{}", fullPath);
            }
        } catch (Exception e) {
            log.error("删除本地文件异常", e);
            throw new BusinessException("删除本地文件失败：" + e.getMessage());
        }
    }
    
    /**
     * 验证文件
     */
    private void validateFile(MultipartFile file) {
        if (file.isEmpty()) {
            throw new BusinessException("文件不能为空");
        }
        
        // 验证文件大小（最大10MB）
        long maxSize = 10 * 1024 * 1024;
        if (file.getSize() > maxSize) {
            throw new BusinessException("文件大小不能超过10MB");
        }
        
        // 验证文件类型（可以根据需要扩展）
        String filename = file.getOriginalFilename();
        if (filename == null || filename.isEmpty()) {
            throw new BusinessException("文件名不能为空");
        }
        
        // 允许的文件扩展名
        String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".zip", ".rar"};
        boolean isAllowed = false;
        for (String ext : allowedExtensions) {
            if (filename.toLowerCase().endsWith(ext)) {
                isAllowed = true;
                break;
            }
        }
        
        if (!isAllowed) {
            throw new BusinessException("不支持的文件类型，仅支持图片、PDF、Office文档和压缩包");
        }
    }
    
    /**
     * 构建文件夹路径
     */
    private String buildFolderPath(String category, String fileType) {
        String year = String.valueOf(LocalDateTime.now().getYear());
        return year + "/" + category + "/" + fileType;
    }
    
    /**
     * 发送审核通知
     */
    private void sendAuditNotification(FileInfo fileInfo) {
        // 通知管理员有新文件需要审核
        // 这里可以查询所有admin角色的用户并发送通知
        messageService.sendMessageToRole(
            "admin",
            "file_audit",
            "新文件待审核",
            "有新的文件需要审核：" + fileInfo.getFileName(),
            fileInfo.getId()
        );
    }
    
    /**
     * 发送审核结果通知
     */
    private void sendAuditResultNotification(FileInfo fileInfo) {
        String title = fileInfo.getAuditStatus() == 1 ? "文件审核通过" : "文件审核驳回";
        String content = "您上传的文件《" + fileInfo.getFileName() + "》" + 
                        (fileInfo.getAuditStatus() == 1 ? "已通过审核" : "审核未通过");
        if (fileInfo.getAuditRemark() != null) {
            content += "。审核意见：" + fileInfo.getAuditRemark();
        }
        
        messageService.sendMessage(
            fileInfo.getUploadUser(),
            "file_audit",
            title,
            content,
            fileInfo.getId()
        );
    }
    
    /**
     * 转换为VO
     */
    private FileInfoVO convertToVO(FileInfo fileInfo) {
        FileInfoVO vo = new FileInfoVO();
        BeanUtils.copyProperties(fileInfo, vo);
        
        // 格式化文件大小
        vo.setFileSizeFormatted(formatFileSize(fileInfo.getFileSize()));
        
        // 设置类型名称
        vo.setFileTypeName(getFileTypeName(fileInfo.getFileType()));
        vo.setCategoryName(getCategoryName(fileInfo.getCategory()));
        vo.setAuditStatusName(getAuditStatusName(fileInfo.getAuditStatus()));
        
        // 设置是否可预览
        vo.setCanPreview(isPreviewable(fileInfo.getFileName()));
        
        // 查询关联信息
        if (fileInfo.getStudentId() != null) {
            Student student = studentMapper.selectById(fileInfo.getStudentId());
            if (student != null) {
                vo.setStudentNo(student.getStudentNo());
                if (student.getUserId() != null) {
                    SysUser user = userMapper.selectById(student.getUserId());
                    if (user != null) {
                        vo.setStudentName(user.getRealName());
                    }
                }
            }
        }
        
        // 查询上传人
        if (fileInfo.getUploadUser() != null) {
            SysUser uploader = userMapper.selectById(fileInfo.getUploadUser());
            if (uploader != null) {
                vo.setUploaderName(uploader.getRealName());
            }
        }
        
        // 查询审核人
        if (fileInfo.getAuditorId() != null) {
            SysUser auditor = userMapper.selectById(fileInfo.getAuditorId());
            if (auditor != null) {
                vo.setAuditorName(auditor.getRealName());
            }
        }
        
        return vo;
    }
    
    /**
     * 批量转换为VO
     */
    private List<FileInfoVO> convertToVOList(List<FileInfo> fileInfoList) {
        return fileInfoList.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }
    
    /**
     * 格式化文件大小
     */
    private String formatFileSize(Long size) {
        if (size == null || size == 0) {
            return "0 B";
        }
        
        String[] units = {"B", "KB", "MB", "GB"};
        int unitIndex = 0;
        double fileSize = size.doubleValue();
        
        while (fileSize >= 1024 && unitIndex < units.length - 1) {
            fileSize /= 1024;
            unitIndex++;
        }
        
        return String.format("%.2f %s", fileSize, units[unitIndex]);
    }
    
    /**
     * 获取文件类型名称
     */
    private String getFileTypeName(String fileType) {
        Map<String, String> typeMap = new HashMap<>();
        typeMap.put("certificate", "证书");
        typeMap.put("medical", "医疗证明");
        typeMap.put("transfer", "转学证明");
        typeMap.put("id_card", "身份证");
        typeMap.put("photo", "照片");
        typeMap.put("academic", "学籍材料");
        typeMap.put("other", "其他");
        return typeMap.getOrDefault(fileType, fileType);
    }
    
    /**
     * 获取分类名称
     */
    private String getCategoryName(String category) {
        Map<String, String> categoryMap = new HashMap<>();
        categoryMap.put("approval", "审批附件");
        categoryMap.put("archive", "档案附件");
        categoryMap.put("certificate", "证书材料");
        return categoryMap.getOrDefault(category, category);
    }
    
    /**
     * 获取审核状态名称
     */
    private String getAuditStatusName(Integer status) {
        if (status == null) return "未知";
        switch (status) {
            case 0: return "待审核";
            case 1: return "审核通过";
            case 2: return "审核驳回";
            default: return "未知";
        }
    }
    
    /**
     * 判断文件是否可预览
     */
    private Boolean isPreviewable(String fileName) {
        if (fileName == null) return false;
        String lowerName = fileName.toLowerCase();
        return lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg") || 
               lowerName.endsWith(".png") || lowerName.endsWith(".gif") || 
               lowerName.endsWith(".pdf");
    }
}

